package org.example.context.support;

import org.example.context.ApplicationContextAware;
import org.example.context.ApplicationEvent;
import org.example.context.ApplicationListener;
import org.example.context.ConfigurableApplicationContext;
import org.example.context.event.ApplicationEventMulticaster;
import org.example.context.event.ContextClosedEvent;
import org.example.context.event.ContextRefreshedEvent;
import org.example.context.event.SimpleApplicationEventMulticaster;
import org.example.core.io.DefaultResourceLoader;
import org.example.exception.BeansException;
import org.example.factory.ConfigurableListableBeanFactory;
import org.example.factory.config.BeanFactoryPostProcessor;
import org.example.factory.config.BeanPostProcessor;
import org.example.factory.config.ConfigurableBeanFactory;
import org.example.factory.support.ApplicationContextAwareProcessor;
import org.example.factory.support.DefaultSingletonBeanRegistry;

import java.util.Collection;
import java.util.Map;

/**
 * @Author: luyg
 * @Date: 2024/09/07/12:40
 * @Description:
 */
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";


    private ApplicationEventMulticaster applicationEventMulticaster;
    @Override
    public Object getBean(String name) throws BeansException {
        return getBeanFactory().getBean(name);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return getBeanFactory().getBean(name, args);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(name, requiredType);
    }

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        return getBeanFactory().getBeansOfType(type);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }

    @Override
    public void refresh() throws BeansException {
        // 1. 创建 BeanFactory，并加载 BeanDefinition
        refreshBeanFactory();
        // 2. 获取 BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        // 3. 添加 ApplicationContextAwareProcessor，让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        // 4. 在 Bean 实例化之前，执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
        invokeBeanFactoryPostProcessors(beanFactory);
        // 5. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
        registerBeanPostProcessors(beanFactory);
        // 6. 初始化事件发布者
        initApplicationMulticaster();
        // 7. 注册监听器
        registerListeners();
        // 8. 提前实例化单例Bean对象
        beanFactory.preInstantiateSingletons();
        // 9. 发布事件
        finishRefresh();
    }

    protected abstract void refreshBeanFactory() throws  BeansException;



    @Override
    public void publishEvent(ApplicationEvent applicationEvent) {
        applicationEventMulticaster.multicastEvent(applicationEvent);
    }


    @Override
    public void close() {
        publishEvent(new ContextClosedEvent(this));
        getBeanFactory().destroySingletons();
    }
    @Override
    public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }
    protected abstract ConfigurableListableBeanFactory getBeanFactory();

    public void initApplicationMulticaster(){
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        applicationEventMulticaster=new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME,applicationEventMulticaster);
    }

    protected void registerListeners(){
        Collection<ApplicationListener> values = getBeansOfType(ApplicationListener.class).values();
        for (ApplicationListener value : values) {
            applicationEventMulticaster.addApplicationListener(value);
        }
    }

    private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanFactoryPostProcessor> beanOfType = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
        for (BeanFactoryPostProcessor value : beanOfType.values()) {
            value.postProcessBeanFactory(beanFactory);
        }
    }

    private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory){
        Map<String, BeanPostProcessor> beanOfType = beanFactory.getBeansOfType(BeanPostProcessor.class);
        for (BeanPostProcessor value : beanOfType.values()) {
            beanFactory.addBeanPostProcessor(value);
        }
    }


    private void finishRefresh(){
        publishEvent(new ContextRefreshedEvent(this));
    }

}
